/*
 *--------------------------------------------------------------------------
 *   Battlefield - A Realtime Network Multiplayer Game
 *   =======================================================
 *   Developed by Group D02 - 2009/2010 Semester 4 - CS2103
 *     Harry Nguyen Duy Hoang <nnduyhoang@yahoo.co.uk>
 *     Kent Chng Siang Rong <fivefootway@gmail.com>
 *     Lim Yong Peng <limpeng1986@gmail.com>
 *     Loh Xiankun <u0807185@nus.edu.sg>
 *   Instructed by
 *     Dr. Damith C.Rajapakse <damith@gmail.com>
 *   =======================================================
 *   $Id: AIManager.java 663 2010-07-30 09:01:11Z Kent $
 *   $LastChangedDate: 2010-07-30 09:01:11 +0000 (Fri, 30 Jul 2010) $
 *   $LastChangedBy: Kent $
 *--------------------------------------------------------------------------
 */
package battlefield.manager;

import battlefield.BattlefieldException;
import battlefield.entity.Game;
import battlefield.entity.Map;
import battlefield.entity.Player;
import battlefield.entity.UnitModel;
import battlefield.state.CellState;
import battlefield.state.GamePhase;
import battlefield.state.PlayerState;
import battlefield.state.UnitType;
import java.util.ArrayList;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Kent
 *
 * This class is essentially the AI of the whole game with 2 different levels of difficulty
 * normal and hard, the AI will decide at random to place weapons or bombs on the map.
 */
public class AIManager extends Thread {

    /**
     *
     */
    public static final int normalMode = 0;
    /**
     *
     */
    public static final int hardMode = 1;
    private final int bombOverEquipmentRatio = 10;
    private final int maxHitMiss = 10;
    private final int maxTries = 5;
    private int modelSize, reloadCounter;
    private Player computerPlayer, opponentPlayer;
    private Game game;
    private Map myMap, opponentMap;
    private UnitModel model;
    private GameManager gameManager;
    private PlayerManager playerManager;
    private MapManager mapManager;
    private UnitManager unitManager;
    private Random generator;
    private UnitModelManager unitModelManager;
    private ArrayList<UnitModel> unitModelList;
    private int difficultyLevel, cheapestEquipment, cheapestWeapon;

    /**
     * Construct an AIManager that manages the artificial intelligence of the computer.
     *
     * @param difficultyLevel define by the AIManager static variable normalMode and hardMode
     * @param game that the computer is going to play in
     * @param computerPlayer the computer
     * @param opponentPlayer the player
     * @param gameManager
     * @param playerManager
     * @param mapManager
     * @param unitManager
     * @param unitModelManager
     */
    public AIManager(int difficultyLevel, Game game, Player computerPlayer, Player opponentPlayer,
            GameManager gameManager, PlayerManager playerManager, MapManager mapManager,
            UnitManager unitManager, UnitModelManager unitModelManager) {
        this.difficultyLevel = difficultyLevel;
        this.game = game;
        this.computerPlayer = computerPlayer;
        this.opponentPlayer = opponentPlayer;
        generator = new Random();
        this.gameManager = gameManager;
        this.playerManager = playerManager;
        this.mapManager = mapManager;
        this.unitManager = unitManager;
        this.unitModelManager = unitModelManager;
        unitModelList = unitModelManager.getUnitModels();
        cheapestEquipment=0;
        cheapestWeapon=0;
    }

    private void tacticalPhase() {
        try {
            int mapX, mapY;
            String mapName = mapManager.getAvailableMaps()[generator.nextInt(mapManager.getAvailableMaps().length)];
            myMap = mapManager.createMap(computerPlayer, mapName);
            game.setPhase(GamePhase.CREATION);
            gameManager.startTacticalPhase(game, computerPlayer);
            modelSize = unitModelList.size();
            while (true) {
                model = unitModelList.get(generator.nextInt(modelSize));
                if ((model.getType().equals(UnitType.EQUIPMENT)) && (computerPlayer.getGold() > model.getPrice())) {
                    while (true) {
                        mapX = generator.nextInt(myMap.getRows());
                        mapY = generator.nextInt(myMap.getColumns());
                        if (mapManager.isUnitPlaceableOnMap(myMap, model, mapX, mapY)) {
                            mapManager.placeUnitOnMap(myMap, unitManager.create(model), mapX, mapY);
                            computerPlayer.setGold(computerPlayer.getGold() - model.getPrice());
                            refreshStats();
                            System.out.println("Placed equipment " + model.getName() + " at " + mapX + " and " + mapY + " Gold left " + computerPlayer.getGold());
                            break;
                        }
                    }
                } else if (getCheapestUnitCost(UnitType.EQUIPMENT)>computerPlayer.getGold()&&computerPlayer.isAlive()) {
                    break;
                }
            }
            computerPlayer.setState(PlayerState.DONE);
            while (!game.isPhaseCompleted()) {
                synchronized (this) {
                    wait(1000);
                }
            }

        } catch (BattlefieldException ex) {
            Logger.getLogger(AIManager.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InterruptedException ie) {
            Logger.getLogger(AIManager.class.getName()).log(Level.SEVERE, null, ie);
        }

    }

    private void warPhase() {
        try {
            int missedCounter = 0;
            synchronized (this) {
                wait(3000); // to compensate for player ui loading time
            }
            int mapX, mapY;
            gameManager.startWarPhase(game);
            modelSize = unitModelManager.getUnitModels().size();
            int counter = 0, tryCounter;
            reloadCounter = 0;

            while (true) {
                tryCounter = 0;
                refreshStats();
                myMap = computerPlayer.getMap();
                opponentMap = opponentPlayer.getMap();
                if (!computerPlayer.isAlive() || !opponentPlayer.isAlive()) {
                    System.out.println("Game over");
                    break;
                }
                model = unitModelList.get(generator.nextInt(modelSize));
                 System.out.println("Model type generated:"+model.getType());
                if(getCheapestUnitCost()>computerPlayer.getGold()){
                    //not enough money for anything
                    //TODO: Remove println
                    System.out.println("Not enough money for anything");
                }else if ((model.getType().equals(UnitType.EQUIPMENT)) && (computerPlayer.getGold() > model.getPrice()) && (counter >= bombOverEquipmentRatio)) {
                    while (true&&tryCounter <= maxTries) {
                        counter = 0;
                        mapX = getNextX();
                        mapY = getNextY();
                        if (mapManager.isUnitPlaceableOnMap(myMap, model, mapX, mapY)) {
                            mapManager.placeUnitOnMap(myMap, unitManager.create(model), mapX, mapY);
                            computerPlayer.setGold(computerPlayer.getGold() - model.getPrice());
                            //TODO: Remove println
                            System.out.println("Placed equipment " + model.getName() + " at " + mapX + " and " + mapY + " Gold left " + computerPlayer.getGold());
                            break;
                        } else {
                            tryCounter++;
                            model = getUnit(UnitType.EQUIPMENT);
                            if(model==null) break;
                        }
                    }
                } else if ((model.getType().equals(UnitType.WEAPON)) && (computerPlayer.getGold() > model.getPrice())) {
                    while (true&&tryCounter<maxTries) {
                        if (missedCounter>=maxHitMiss&&difficultyLevel==hardMode) {
                            int loc[] = getUnitLocation();
                            mapX = loc[0];
                            mapY = loc[1];
                        } else {
                            mapX = getNextX();
                            mapY = getNextY();
                        }
                        if (mapManager.isWeaponPlaceableOnMap(opponentMap, unitManager.create(model), mapX, mapY)) {
                            if (mapManager.useWeaponOnMap(opponentMap, unitManager.create(model), mapX, mapY)) {
                                missedCounter = 0;
                            } else {
                                missedCounter++;
                                //TODO: Remove println
                                System.out.println("Missed counter " + missedCounter);
                            }
                            computerPlayer.setGold(computerPlayer.getGold() - model.getPrice());
                            counter++;
                            //TODO: Remove println
                            System.out.println("Placed equipment " + model.getName() + " at " + mapX + " and " + mapY + " Gold left " + computerPlayer.getGold());
                            break;
                        } else {
                            tryCounter++;
                            model = getUnit(UnitType.WEAPON);
                            if(model==null) break;
                        }
                    }
                }
                synchronized (this) {
                    wait(1000);
                    reloadCounter++;
                }
                //TODO: Remove println
                System.out.println("Reloading counter in "+(game.getGoldReloadDuration()-reloadCounter)+" seconds");
                reloadComputerGold();
            }
        } catch (BattlefieldException ex) {
            Logger.getLogger(AIManager.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InterruptedException ie) {
            Logger.getLogger(AIManager.class.getName()).log(Level.SEVERE, null, ie);
        }
    }

    private void refreshStats() {
        try {
            computerPlayer = playerManager.updateUnitCount(computerPlayer);
            opponentPlayer = playerManager.updateUnitCount(opponentPlayer);
        } catch (BattlefieldException ex) {
            Logger.getLogger(AIManager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    private void reloadComputerGold(){
        if (reloadCounter >= game.getGoldReloadDuration()) {
            computerPlayer.setGold(computerPlayer.getGold() + game.getGoldReloadAmount() + 2000);
            reloadCounter = 0;
        }
        
    }

    private int[] getUnitLocation() {
        int[] unitLocation = new int[2];
        for (int i = 0; i < opponentMap.getRows(); i++) {
            for (int j = 0; j < opponentMap.getColumns(); j++) {
                if ((opponentMap.hasUnitAt(i, j)) && (opponentMap.getCellAt(i, j).getState().equals(CellState.NORMAL))) {
                    unitLocation[0] = i;
                    unitLocation[1] = j;
                    return unitLocation;
                }
            }
        }
        return unitLocation;
    }

    private UnitModel getUnit(UnitType unitType) {
        int i;
        for (i = 0; i < 20; i++) {
            model = unitModelList.get(generator.nextInt(modelSize));
            if (model.getType() == unitType && model.getPrice() < computerPlayer.getGold()) return model;
        }
        for (i = 0; i < unitModelList.size(); i++) {
            if (unitModelList.get(i).getType() == unitType && unitModelList.get(i).getPrice() < computerPlayer.getGold())
                return unitModelList.get(i);
        }
        return null;
    }

    private int getCheapestUnitCost(){

        if(cheapestWeapon!=0&&cheapestEquipment!=0) 
            return (cheapestWeapon>cheapestEquipment) ? cheapestEquipment:cheapestWeapon;

        int cheapestUnitCost=0;
        for(int i=0;i<unitModelList.size();i++){
            if(!(unitModelList.get(i).getType()==UnitType.TOOL)&&(cheapestUnitCost==0)){
                cheapestUnitCost = unitModelList.get(i).getPrice();
            }else{
                if(!(unitModelList.get(i).getType()==UnitType.TOOL)&&(unitModelList.get(i).getPrice()<cheapestUnitCost))
                    cheapestUnitCost = unitModelList.get(i).getPrice();
            }
        }
        return cheapestUnitCost;
    }
    private int getCheapestUnitCost(UnitType unitType){
        if(unitType==UnitType.EQUIPMENT&&cheapestEquipment!=0) return cheapestEquipment;
        else if((unitType==UnitType.WEAPON&&cheapestWeapon!=0)) return cheapestWeapon;

        int cheapestUnitCost=0;
        for(int i=0;i<unitModelList.size();i++){
                if((unitModelList.get(i).getType()==unitType)&&(cheapestUnitCost==0)){
                cheapestUnitCost = unitModelList.get(i).getPrice();
            }else{
                if((unitModelList.get(i).getType()==unitType)&&(unitModelList.get(i).getPrice()<cheapestUnitCost))
                    cheapestUnitCost = unitModelList.get(i).getPrice();
            }
        }
        if(unitType==UnitType.EQUIPMENT&&cheapestEquipment==0) cheapestEquipment=cheapestUnitCost;
        else if((unitType==UnitType.WEAPON&&cheapestWeapon==0)) cheapestWeapon=cheapestUnitCost;
        return cheapestUnitCost;
    }

    private int getNextX() {
        return generator.nextInt(myMap.getRows());
    }

    private int getNextY() {
        return generator.nextInt(myMap.getColumns());
    }

    @Override
    public void run() {
        tacticalPhase();
        warPhase();
    }
}
